package tw.idv.liuken.android.sip;

import java.text.ParseException;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.ToneGenerator;
import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipRegistrationListener;
import android.net.sip.SipSession;
import android.util.Log;

public class SipWrapper extends BroadcastReceiver implements SipRegistrationListener {
	private final String TAG = "SipWrapper";
	public static String INCOMING_CALL_INTENT = "tw.org.itri.itris.n200.sip.INCOMING_CALL";
	
	private SipManager mSipManager;
	private Context mContext;
	
	private String mSipServer;
	private String mSipAccount;
	private String mSipPassword;
	private SipProfile.Builder mSipProfileBuilder;
	private SipProfile mSipProfile;
	
	private SipSession mRegisterSession;
	private InviteSipSessionListener mInviteSipSessionListener;
	private IncomingCallSipSessionListener mIncomingCallSipSessionListener;
	private SipCallback mSipCallback;
	
	private SipAudioCall mSipAudioCall;
	private SipVideoCall mSipVideoCall;
	private SipSession mInviteSession;
	
	private Intent mIncomingCallIntent;
	private SipSession mIncomingCallSession;
	
	private enum SipState {NONE, INVITE, INCOMING};
	private SipState state = SipState.NONE;
	
	private ToneGenerator mToneGenerator;
	private Ringtone mRingtone;
	
	public SipWrapper(Context context) {
		mContext = context;
		if (mSipManager == null)
			mSipManager = SipManager.newInstance(context);
		
		IntentFilter filter = new IntentFilter();
        filter.addAction(SipWrapper.INCOMING_CALL_INTENT);
        mContext.registerReceiver(this, filter);
        mInviteSipSessionListener = new InviteSipSessionListener();
        mIncomingCallSipSessionListener = new IncomingCallSipSessionListener();
        mToneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, ToneGenerator.MAX_VOLUME);
        mRingtone = RingtoneManager.getRingtone(mContext, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE));
	}
	
	@Override
    public void onReceive(Context context, Intent intent) {
		android.util.Log.v(TAG, "got incoming call");
		if (mSipManager == null) return;
		mIncomingCallIntent = intent;
		
		try {
			mIncomingCallSession = mSipManager.getSessionFor(intent);
			mIncomingCallSession.setListener(mIncomingCallSipSessionListener);			
			mSipAudioCall = new SipAudioCall(mContext, mSipProfile);
			mSipVideoCall = new SipVideoCall();
			mSipVideoCall.setPeerSD(intent.getStringExtra(SipManager.EXTRA_OFFER_SD));
			mSipAudioCall.attachCall(mIncomingCallSession, intent.getStringExtra(SipManager.EXTRA_OFFER_SD));
		} catch (SipException e) {
			e.printStackTrace();
		}
		if (mRingtone != null)
			mRingtone.play();
		
		if (mSipCallback != null)
			mSipCallback.onInComingCall(intent.getStringExtra(mIncomingCallSession.getPeerProfile().getUserName()));
		state = SipState.INCOMING;		
		android.util.Log.v(TAG, "InComingCall " + mIncomingCallSession.getPeerProfile().getUserName());
	}
	
	public boolean isSipSupport() {
		boolean voipSupporting = SipManager.isVoipSupported(mContext);
		boolean apiSupported = SipManager.isApiSupported(mContext);
		boolean sipWifiOnly = SipManager.isSipWifiOnly(mContext);
		
		android.util.Log.v(TAG, "SipManager: isVoipSupported = " + voipSupporting + ", isApiSupported = " + apiSupported
				+ ", isSipWifiOnly = " + sipWifiOnly);
		
		return (voipSupporting & apiSupported);
	}
	
	public boolean setSipProfile(String sipUri, String account, String password) {
		try {
			mSipProfileBuilder = new SipProfile.Builder(account, sipUri);
			mSipProfileBuilder.setPassword(password);
			//mSipProfileBuilder.setPassword(password).setAutoRegistration(true);
			mSipProfile = mSipProfileBuilder.build();
			mSipServer = sipUri;
			mSipAccount = account;
			mSipPassword = password;
		} catch (ParseException e) {
			e.printStackTrace();
			android.util.Log.v(TAG, "Parse SipProfile Error!");
			mSipProfile = null;
			mSipProfileBuilder = null;
			return false;
		}
		android.util.Log.v(TAG, "Set SipProfile: server = " + sipUri + ", account = " + account 
				+ ", autoRegistration = " + mSipProfile.getAutoRegistration());
		return true;
	}
	
	public void open() {
		if (mSipProfile != null) {
			try {
				Intent i = new Intent();
	            i.setAction(SipWrapper.INCOMING_CALL_INTENT);
	            PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, Intent.FILL_IN_DATA);
	            mSipManager.open(mSipProfile, pi, this);
				android.util.Log.v(TAG, mSipProfile + " is opend: " + mSipManager.isOpened(mSipProfile.getUriString()));
			} catch (SipException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void makeCall(String id) {
		if (mSipProfile == null) return;
		try {
			SimpleSessionDescription offer;
			String uri = "sip:" + id + "@" + mSipServer;
			mSipAudioCall = new SipAudioCall(mContext, mSipProfile);
			mSipVideoCall = new SipVideoCall();
			mInviteSession = mSipManager.createSipSession(mSipProfile, mInviteSipSessionListener);
			offer = mSipAudioCall.makeCall(mSipProfile, mInviteSession, 30);
			mSipVideoCall.createOffer(offer);
			mInviteSession.makeCall(new SipProfile.Builder(uri).build(), offer.encode(), 30);
		} catch (SipException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}
		state = SipState.INVITE;
	}
	
	public void answerCall() {
		if (mIncomingCallSession == null || mSipAudioCall == null) return;
		try {
			SimpleSessionDescription answer = mSipAudioCall.answerCall(30);
			mSipVideoCall.createAnswer(answer);
			mIncomingCallSession.answerCall(answer.encode(), 30);
		} catch (SipException e) {
			e.printStackTrace();
		}
		AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
		am.setMode(AudioManager.MODE_IN_CALL);
		mSipAudioCall.startAudio();
		mSipVideoCall.startVideo();
		//mSipAudioCall.setSpeakerMode(true);
	}
	
	public void setSipCallback(SipCallback callback) {
		mSipCallback = callback;
	}
	
	public void register() {
		if (mSipProfile != null) {
			try {
				android.util.Log.v(TAG, mSipProfile + " is opend: " + mSipManager.isOpened(mSipProfile.getUriString()));
				mRegisterSession = mSipManager.createSipSession(mSipProfile, mInviteSipSessionListener);
				android.util.Log.v(TAG, "SipSession is in " + SipSession.State.toString(mRegisterSession.getState()));
				mRegisterSession.register(10);
				android.util.Log.v(TAG, "SipSession is in " + SipSession.State.toString(mRegisterSession.getState()));
			} catch (SipException e) {
				e.printStackTrace();
			}
		}
	}
	
	public boolean isOpend() {
		boolean result = false;
		try {
			result =  mSipManager.isOpened(mSipProfile.toString());
		} catch (SipException e) {
			e.printStackTrace();
		}
		return result;
	}

	public boolean isRegistered() {
		boolean result = false;
		try {
			result =  mSipManager.isRegistered(mSipProfile.toString());
		} catch (SipException e) {
			e.printStackTrace();
		}
		return result;
	}
	
    public void close() {
    	try {
    		if (mSipProfile != null)
    			mSipManager.close(mSipProfile.getUriString());
			mContext.unregisterReceiver(this);
		} catch (SipException e) {
			e.printStackTrace();
		}
    }
    
    public void toggleMuteAudio() {
    	if (mSipAudioCall == null) return;
    	mSipAudioCall.toggleMute();
    }
    
    public void muteAudio() {
    	if (mSipAudioCall == null) return;
    	if (mSipAudioCall.isMuted() == false) return;
    	mSipAudioCall.toggleMute();
    }
    
    public void unmuteAudio() {
    	if (mSipAudioCall == null) return;
    	if (mSipAudioCall.isMuted() == true) return;
    	mSipAudioCall.toggleMute();
    }
    
    public void sendDtmf(int code) {
    	if (mSipAudioCall == null) return;
    	mSipAudioCall.sendDtmf(code);
    }
    
    boolean mHold = false;
    public void changeCall() {
    	if (mSipAudioCall == null) return;
    	try {
    		if (mHold) {
    			mSipAudioCall.continueCall(10);
    			mHold = false;
    		} else {
    			mSipAudioCall.holdCall(10);
    			mHold = true;
    		}
		} catch (SipException e) {
			e.printStackTrace();
		}
    }
    
    public void endCall() {
		if (state == SipState.NONE) return;
		try {
			mSipAudioCall.endCall();
		} catch (SipException e) {
			e.printStackTrace();
		}
		if (state == SipState.INCOMING) {
			mIncomingCallSession.endCall();
		} else if (state == SipState.INVITE) { 
			mInviteSession.endCall();
		}
		//callEnd();
	}
    
    void callEnd() {
    	if (state == SipState.INCOMING) {
			mIncomingCallSession = null;
			if (mRingtone != null && mRingtone.isPlaying())
				mRingtone.stop();
		} else if (state == SipState.INVITE) { 
			mInviteSession = null;
			mToneGenerator.stopTone();
		}
    	if (mSipAudioCall != null) {
    		mSipAudioCall.close();
    		mSipAudioCall = null;
    	}
    	AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
		am.setMode(AudioManager.MODE_NORMAL);
		state = SipState.NONE;
    }
	
	private class InviteSipSessionListener extends SipSession.Listener {		
		@Override
		public void onCallChangeFailed(SipSession session, int errorCode,
				String errorMessage) {
			android.util.Log.v(TAG, "InviteSipSessionListener: onCallChangeFailed");
		}

		@Override
		public void onRegistering(SipSession session) {
			android.util.Log.v(TAG, "InviteSipSessionListener: onRegistering");
		}

		@Override
		public void onRegistrationDone(SipSession session, int duration) {
			android.util.Log.v(TAG, "InviteSipSessionListener: onRegistrationDone");
		}

		@Override
		public void onRegistrationFailed(SipSession session, int errorCode,
				String errorMessage) {
			android.util.Log.v(TAG, "InviteSipSessionListener: onRegistrationFailed");
		}

		@Override
		public void onRegistrationTimeout(SipSession session) {
			android.util.Log.v(TAG, "InviteSipSessionListener: onRegistrationTimeout");
		}

		@Override
		public void onRinging(SipSession session, SipProfile caller,
				String sessionDescription) {
			android.util.Log.v(TAG, "InviteSipSessionListener: onRinging");
		}

		@Override
		public void onCallBusy(SipSession session) {
			android.util.Log.v(TAG, "InviteSipSessionListener: Call Busy");
			mToneGenerator.stopTone();
			if (mSipCallback != null)
				mSipCallback.onCallBusy();
		}

		@Override
		public void onCallEnded(SipSession session) {
			android.util.Log.v(TAG, "InviteSipSessionListener: Call Ended");
			SipWrapper.this.callEnd();
			if (mSipCallback != null)
				mSipCallback.onEndCall();
		}

		@Override
		public void onCallEstablished(SipSession session,
				String sessionDescription) {
			android.util.Log.v(TAG, "InviteSipSessionListener: Call Established");
			mToneGenerator.stopTone();
			if (mSipCallback != null)
				mSipCallback.onIncall();
			
			AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
			//am.setMode(AudioManager.MODE_IN_CALL);
			mSipAudioCall.setPeerSD(sessionDescription);
			mSipVideoCall.setPeerSD(sessionDescription);
			mSipAudioCall.startAudio();
			mSipVideoCall.startVideo();
			//mSipAudioCall.setSpeakerMode(true);
		}

		@Override
		public void onCalling(SipSession session) {
			android.util.Log.v(TAG, "InviteSipSessionListener: Calling");
		}

		@Override
		public void onError(SipSession session, int errorCode,
				String errorMessage) {
			android.util.Log.v(TAG, "InviteSipSessionListener: Error");
		}

		@Override
		public void onRingingBack(SipSession session) {
			android.util.Log.v(TAG, "InviteSipSessionListener: RingingBack");
			mToneGenerator.startTone(ToneGenerator.TONE_SUP_RINGTONE);
		}
	}
	
	private class IncomingCallSipSessionListener extends SipSession.Listener {		
		@Override
		public void onCallChangeFailed(SipSession session, int errorCode,
				String errorMessage) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onCallChangeFailed");
		}

		@Override
		public void onCalling(SipSession session) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onCalling");
		}

		@Override
		public void onRegistering(SipSession session) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onRegistering");
		}

		@Override
		public void onRegistrationDone(SipSession session, int duration) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onRegistrationDone");
		}

		@Override
		public void onRegistrationFailed(SipSession session, int errorCode,
				String errorMessage) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onRegistrationFailed");
		}

		@Override
		public void onRegistrationTimeout(SipSession session) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onRegistrationTimeout");
		}

		@Override
		public void onRinging(SipSession session, SipProfile caller,
				String sessionDescription) {
			/*
			 * This function is called while a re-INVITE.
			 * From Android SDK: Called when an INVITE request is received.
			 */
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onRinging");
			/*
			try {
                String answer = createAnswer(sessionDescription).encode();
                mSipSession.answerCall(answer, SESSION_TIMEOUT);
            } catch (Throwable e) {
                Log.e(TAG, "onRinging()", e);
                session.endCall();
            }*/
		}

		@Override
		public void onRingingBack(SipSession session) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: onRingingBack");
		}

		@Override
		public void onCallBusy(SipSession session) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: Call Busy");
			if (mRingtone != null && mRingtone.isPlaying())
				mRingtone.stop();
			if (mSipCallback != null)
				mSipCallback.onCallBusy();
		}

		@Override
		public void onCallEnded(SipSession session) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: Call Ended");
			SipWrapper.this.callEnd();
			if (mSipCallback != null)
				mSipCallback.onEndCall();
		}

		@Override
		public void onCallEstablished(SipSession session,
				String sessionDescription) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: Call Established");
			if (mRingtone != null && mRingtone.isPlaying())
				mRingtone.stop();
			if (mSipCallback != null)
				mSipCallback.onIncall();
		}

		@Override
		public void onError(SipSession session, int errorCode,
				String errorMessage) {
			android.util.Log.v(TAG, "InComingCallSipSessionListener: Error");
		}
	}
	
	public void onRegistering(String localProfileUri) {
		android.util.Log.v(TAG, "Registering with SIP Server...");
    }

    public void onRegistrationDone(String localProfileUri, long expiryTime) {
    	android.util.Log.v(TAG, "Ready");
    }

    public void onRegistrationFailed(String localProfileUri, int errorCode,
            String errorMessage) {
    	android.util.Log.v(TAG, "Registration failed.  Please check settings.");
    }
}
